﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Web;
using System.Data;
using System.Data.Linq.SqlClient;
using KC.DataAccess.BlogSlinger;
using KC.DataAccess.Global;
using Facebook;
using System.Xml;

namespace KC.Business
{
	public static class BlogSlinger
	{
		public static IOrderedQueryable<Post> GetRecentPosts(ref BlogSlingerDataContext dc)
		{
			return (from p in dc.Posts
					where p.PublishDate <= DateTime.Now && p.Status == "Active"
					orderby p.PublishDate descending
					select p);
		}
		public static string DecodeToken(string queryResult, out int expires)
		{
			// access_token=156267124416417|2.fQh4quPTSFYFYfbpiuMgmw__.3600.1289764800-1007503800|d1VajESvvdjAb4NJp6C_ULqihvQ&expires=5974
			NameValueCollection nvc = HttpUtility.ParseQueryString(@"http://localhost/?a&" + queryResult);
			expires = int.Parse(nvc["expires"]);
			return nvc["access_token"];
		}
		public static string DecodeToken(string queryResult)
		{
			// access_token=156267124416417|2.fQh4quPTSFYFYfbpiuMgmw__.3600.1289764800-1007503800|d1VajESvvdjAb4NJp6C_ULqihvQ&expires=5974
			return HttpUtility.ParseQueryString(@"http://localhost/?a&" + queryResult)["access_token"];
		}
		public static User RecordLogin(ref BlogSlingerDataContext dc, string userid, string username, string token)
		{
			FacebookAPI api = new FacebookAPI(token);
			var users = from u in dc.Users where u.FacebookUserID == userid select u;
			Dictionary<string, JSONObject> dict = api.Get("/me").Dictionary;
			User user;
			if (users.Count() == 0){
				user = new User()
				{
					FacebookUserID = userid,
					FacebookName = username,
					DisplayName = username,
					// facebook profile = api.Get("/me").Dictionary["link"].String,
					Email = dict.ContainsKey("email") ? dict["email"].String : "",
					RegistrationDate = DateTime.Now,
					StatusID = (from s in dc.UserStatuses where s.status == "Active" select s).First().ID,
					URL = dict.ContainsKey("website") ? dict["website"].String : (api.Get("/me").Dictionary["link"].String ?? "")
				};
				dc.Users.InsertOnSubmit(user);
			}
			else
			{
				user = users.First();
				user.FacebookUserID = userid;
				user.FacebookName = username;
			}
			dc.SubmitChanges();
			return user;
		}

		public static IQueryable<UserRole> GetBlogsByUser(ref BlogSlingerDataContext dc, int userid)
		{
			return from b in dc.UserRoles
				   where b.UserID == userid
				   orderby b.Blog.Name
				   select b;
		}
		public static int CreateBlog(ref BlogSlingerDataContext dc, int userid, string name)
		{
			Blog blog = new Blog() { DateCreated = DateTime.Now, Name = name };
			dc.Blogs.InsertOnSubmit(blog);
			dc.SubmitChanges();

			UserRole role = new UserRole() { BlogID = blog.ID, UserID = userid, RelationshipType = "Creator" };
			dc.UserRoles.InsertOnSubmit(role);
			dc.SubmitChanges();
			return blog.ID;
		}
		public static IQueryable<Post> GetPostsByBlog(ref BlogSlingerDataContext dc, int BlogID, int qty)
		{
			return (from p in dc.Posts
					where p.BlogID == BlogID
						&& p.PostType == 1
					orderby p.ID descending
					select p).Take(qty);
		}
		public static IQueryable<Post> GetPostsByCategory(ref BlogSlingerDataContext dc, int CatID)
		{
			return from p in dc.PostToCategories
				   where p.CategoryID == CatID
				   orderby p.Post.PublishDate descending, p.Post.ID descending 
				   select p.Post;
		}
		public static Category GetCategory(ref BlogSlingerDataContext dc, int ID)
		{
			var cats = from p in dc.Categories where p.ID == ID select p;
			return cats.Count() == 0 ? null : cats.First();
		}
		public static IQueryable<Post> GetArticlesByBlog(ref BlogSlingerDataContext dc, int BlogID, int qty)
		{
			return (from p in dc.Posts
					where p.BlogID == BlogID
						&& p.PostType == 2
					orderby p.ID descending
					select p).Take(qty);
		}
		public static IQueryable<Category> GetCategories(ref BlogSlingerDataContext dc)
		{
			return from c in dc.Categories
				   orderby c.Name
				   select c;
		}

		public static Post GetPost(ref BlogSlingerDataContext dc, string title)
		{
			var posts = from p in dc.Posts
						where p.FriendlyName == title
						select p;
			if (posts.Count() == 0)
				posts = from p in dc.Posts
						where p.FriendlyName == HttpUtility.UrlEncode(title)
						select p;
			return posts.Count() == 0 ? null : posts.First();
		}
		public static Post GetPost(ref BlogSlingerDataContext dc, int ID)
		{
			var posts = from p in dc.Posts
						where p.ID == ID
						select p;
			return posts.Count() == 0 ? null : posts.First();
		}

		// user stuff
		public static User GetUserByPost(ref BlogSlingerDataContext dc, Post post) {
			var users = from r in dc.UserRoles
						where r.BlogID == post.BlogID
						orderby r.ID descending
						select r.User;
			if (users.Count() == 0)
				return null;
			return users.First();
		}
		public static User GetUserByBlog(ref BlogSlingerDataContext dc, Blog blog)
		{
			var users = from r in dc.UserRoles
						where r.BlogID == blog.ID
						orderby r.ID descending
						select r.User;
			if (users.Count() == 0)
				return null;
			return users.First();
		}
		public static string GetUserImageURL(ref BlogSlingerDataContext dc, Post post)
		{
			var users = from r in dc.UserRoles
						where r.BlogID == post.BlogID
						orderby r.ID descending
						select r.User;
			if (users.Count() == 0)
				return null;
			return string.Format("https://graph.facebook.com/{0}/picture", users.First().FacebookUserID);
		}
		public static string GetUserImageURL(ref BlogSlingerDataContext dc, User user)
		{
			var users = from u in dc.Users
						where u.ID == user.ID
						orderby u.ID descending
						select u;
			if (users.Count() == 0)
				return null;
			return string.Format("https://graph.facebook.com/{0}/picture", users.First().FacebookUserID);
		}
		public static string GetUserFacebookURL(ref BlogSlingerDataContext dc, int UserID)
		{
			var users = from u in dc.Users where u.ID == UserID select u;
			if (users.Count() == 0)
				return null;
			return string.Format("http://www.facebook.com/profile.php?id={0}", users.First().FacebookUserID);
		}

		public static int? SavePost(ref BlogSlingerDataContext dc, int? PostID, int AuthorUserID, int BlogID, bool isEnabled, string body, string title, string excerpt, DateTime publishDate, bool isAllowComments, int PostType, List<Category> Categories)
		{
			if (PostID == null || (int)PostID <= 0)
				PostID = null;

			Post post;
			if (PostID != null) {
				var posts = from p in dc.Posts where p.ID == PostID.Value select p;
				if (posts.Count() == 0) return null; // TODO: Log warning
				post = posts.First(); }
			else post = new Post();

			post.AuthorUserID = AuthorUserID;
			post.BlogID = BlogID;
			post.Status = isEnabled ? "Active" : "Draft";
			post.Title = title;
			post.Body = body;
			post.Excerpt = excerpt ?? "";
			post.PublishDate = publishDate;
			post.isAllowComments = isAllowComments;
			post.PostType = PostType;
			post.ModifyDate = DateTime.Now;

			if (PostID == null) post.CreateDate = DateTime.Now;
			if (string.IsNullOrEmpty(post.FriendlyName)) post.FriendlyName = BuildFriendlyName(ref dc, post.Title, PostID);
			try {
				if (post.ID <= 0) dc.Posts.InsertOnSubmit(post);
				dc.SubmitChanges(); }
			catch { return null; } // TODO: Log Error

			// do categories
			try
			{
				dc.PostToCategories.DeleteAllOnSubmit(from p in dc.PostToCategories where p.PostID == post.ID select p);
				dc.SubmitChanges();
			}
			catch (NullReferenceException) { }
			catch { throw; } // TODO: Log Error
			foreach (Category cat in Categories)
				dc.PostToCategories.InsertOnSubmit(new PostToCategory() { PostID = post.ID, CategoryID = cat.ID });
			try { dc.SubmitChanges();}
			catch { return null; } // TODO: Log Error

			return (int?)post.ID;
		}
		private static string BuildFriendlyName(ref BlogSlingerDataContext dc, string input, int? PostID)
		{
			string name = HttpUtility.UrlEncode(input);
			if ((PostID == null && (from p in dc.Posts where p.FriendlyName == name select p).Count() == 0) || (PostID != null && (from p in dc.Posts where p.FriendlyName == name && p.ID != PostID.Value select p).Count() == 0))
				return name.Length <= 1024 ? name : name.Substring(name.Length - 1024);

			for (int ID = 0; ID < 1024; ID++)
			{
				if ((PostID == null && (from p in dc.Posts where p.FriendlyName == name select p).Count() == 0) || (PostID != null && (from p in dc.Posts where p.FriendlyName == name && p.ID != PostID.Value select p).Count() == 0))
					return name.Length <= 1024 ? name : name.Substring(name.Length - 1024);
			}
			return null;
		}
		public static void TogglePostEnabled(ref BlogSlingerDataContext dc, int PostID, bool makeActive)
		{
			var posts = from p in dc.Posts where p.ID == PostID select p;
			if (posts.Count() == 0) return;
			posts.First().Status = makeActive ? "Active" : "Inactive";
			dc.SubmitChanges();
		}

		public static IOrderedQueryable<Blog> GetBlogs(ref BlogSlingerDataContext dc)
		{
			return from b in dc.Blogs
							 where b.Posts.Count() > 0
							 orderby b.Name
							 select b;
		}
		public static int GetPostCount(ref BlogSlingerDataContext dc, int BlogID)
		{
			return (from p in dc.Posts
					where p.BlogID == BlogID
						&& p.PostType == 1
						&& p.Status == "Active"
					select p).Count();
		}
		public static Blog GetBlog(ref BlogSlingerDataContext dc, int BlogID)
		{
			var blogs = from b in dc.Blogs where b.ID == BlogID select b;
			return blogs.Count() > 0 ? blogs.First() : null;
		}
		/// <summary>Builds the category links for a Post.</summary>
		/// <param name="post"></param>
		/// <returns></returns>
		public static string BuildCategoryLinks(Post post)
		{
			string result = "";
			foreach (PostToCategory ptc in post.PostToCategories)
				result += "<a href=\"Categories.aspx?ID=" + ptc.Category.ID.ToString() + "\">" + ptc.Category.Name + "</a>, ";
			result = result.TrimEnd(new char[] { ',', ' ' });
			return result;
		}

		public static void DeletePost(ref BlogSlingerDataContext dc, int PostID)
		{
			dc.PostToCategories.DeleteAllOnSubmit(from p in dc.PostToCategories where p.PostID == PostID select p);
			dc.SubmitChanges();

			dc.Posts.DeleteAllOnSubmit(from p in dc.Posts where p.ID == PostID select p);
			dc.SubmitChanges();
		}
		public static IOrderedQueryable<Category> GetCategories(ref BlogSlingerDataContext dc, int BlogID)
		{
			return from c in dc.Categories
				   where c.BlogID == BlogID
				   orderby c.Name
				   select c;
		}
		public static void SaveCategory(ref BlogSlingerDataContext dc, string name, string description, int blogID, int? ID = null)
		{
			if (ID == null)
			{
				dc.Categories.InsertOnSubmit(new Category() { Name = KC.Business.Text.StripHTML(name), Description = KC.Business.Text.StripHTML(description), BlogID = blogID });
				dc.SubmitChanges();
			}
			else
			{
				var cats = from c in dc.Categories
						   where c.BlogID == blogID && c.ID == ID.Value
						   select c;
				if (cats.Count() == 0)
					SaveCategory(ref dc, name, description, blogID);
				else
				{
					Category cat = cats.First();
					cat.Name = KC.Business.Text.StripHTML(name);
					cat.Description = KC.Business.Text.StripHTML(description);
					cat.BlogID = blogID;
					dc.SubmitChanges();
				}
			}
		}
		public static void DeleteCategory(ref BlogSlingerDataContext dc, int CatID)
		{
			// Remove this category from all Posts
			dc.PostToCategories.DeleteAllOnSubmit(from c in dc.PostToCategories where c.CategoryID == CatID select c);
			dc.SubmitChanges();

			// Remove this category
			dc.Categories.DeleteAllOnSubmit(from c in dc.Categories where c.ID == CatID select c);
			dc.SubmitChanges();
		}

		public static void LogActivity(ref BlogSlingerDataContext dc, int? userID, string ip, string action, string detail, string request, string referrer)
		{
			try
			{
				dc.Activities.InsertOnSubmit(new Activity() { UserID = userID, IP = ip, Action = action, Detail = detail, Request = request, Referrer = referrer, DateTime = DateTime.Now });
				dc.SubmitChanges();
			}
			catch { } // TODO: Log error
		}
		public static void LogActivity(ref BlogSlingerDataContext dc, User user, string ip, string action, string detail, string request, string referrer)
		{
			try
			{
				dc.Activities.InsertOnSubmit(new Activity() { UserID = (user == null ? (int?)null : user.ID), IP = ip, Action = action, Detail = detail, Request = request, Referrer = referrer, DateTime = DateTime.Now });
				dc.SubmitChanges();
			}
			catch { } // TODO: Log error
		}

		// stats
		public static void GetDailyPageviews(string cs, out int Avg, out int Max)
		{
			DataTable dt = Global.CallSql_Sproc(cs, "GetDailyPageviews");
			Avg = int.Parse(dt.Rows[0]["avg"].ToString());
			Max = int.Parse(dt.Rows[0]["max"].ToString());
		}

		// search
		public static IQueryable<User> SearchUsers(ref BlogSlingerDataContext dc, string query)
		{
			return from u in dc.Users
				   where SqlMethods.Like(u.DisplayName, "%" + query + "%")
				   orderby u.UserRoles.Count(), u.ID
				   select u;
		}
		public static IQueryable<Blog> SearchBlogs(ref BlogSlingerDataContext dc, string query){
			return from b in dc.Blogs
				   where SqlMethods.Like(b.Name, "%" + query + "%")
					|| SqlMethods.Like(b.Description, "%" + query + "%")
				   orderby b.Posts.Count() descending, b.Name
				   select b;
		}
		public static IQueryable<Post> SearchPosts(ref BlogSlingerDataContext dc, string query)
		{
			return from p in dc.Posts
				   where SqlMethods.Like(p.Title, "%" + query + "%")
					|| SqlMethods.Like(p.Excerpt, "%" + query + "%")
					|| SqlMethods.Like(p.Body, "%" + query + "%")
				   orderby p.PublishDate descending
				   select p;
		}

		// RSS, Sitemaps, and other XML
		public static string FormatForXML(string data) {
			return data
				.Replace("&", "&amp;")
				.Replace("\"", "&quot;")
				.Replace("\'", "&apos;")
				.Replace("<", "&lt;")
				.Replace(">", "&gt;");
		}
		public static XmlDocument BuildSitemapXML(ref BlogSlingerDataContext dc)
		{
			XmlDocument xDoc = new XmlDocument();
			XmlElement xRoot = xDoc.CreateElement("urlset", @"http://www.sitemaps.org/schemas/sitemap/0.9");
			xDoc.AppendChild(xRoot);
			

			xRoot.AppendChild(BuildSitemapUrlNode(ref xDoc, "http://blogs.code-slinger.com", (from p in dc.Posts orderby p.ID descending select p).First().PublishDate, "hourly", "1.0"));
			xRoot.AppendChild(BuildSitemapUrlNode(ref xDoc, "http://blogs.code-slinger.com/live.aspx", DateTime.Now, "daily", "1.0"));
			xRoot.AppendChild(BuildSitemapUrlNode(ref xDoc, "http://blogs.code-slinger.com/rss.aspx", DateTime.Now, "daily", "1.0"));
			xRoot.AppendChild(BuildSitemapUrlNode(ref xDoc, "http://blogs.code-slinger.com/About.aspx", DateTime.Now, "monthly", "1.0"));
			xRoot.AppendChild(BuildSitemapUrlNode(ref xDoc, "http://blogs.code-slinger.com/Blog.aspx", DateTime.Now, "monthly", "1.0"));
			xRoot.AppendChild(BuildSitemapUrlNode(ref xDoc, "http://blogs.code-slinger.com/Browse.aspx", DateTime.Now, "monthly", "1.0"));
			xRoot.AppendChild(BuildSitemapUrlNode(ref xDoc, "http://blogs.code-slinger.com/Categories.aspx", DateTime.Now, "monthly", "1.0"));
			xRoot.AppendChild(BuildSitemapUrlNode(ref xDoc, "http://blogs.code-slinger.com/legal.aspx", DateTime.Now, "monthly", "1.0"));

			// Append blog master URLs
			foreach (Blog blog in from b in dc.Blogs orderby b.Name select b)
				xRoot.AppendChild(BuildSitemapUrlNode(ref xDoc,
					"http://blogs.code-slinger.com/blog.aspx?ID=" + blog.ID.ToString(),
					blog.Posts.Count() > 0 ? (from p in dc.Posts where p.BlogID == blog.ID orderby p.ID descending select p).First().PublishDate : blog.DateCreated,
					"daily", "0.5"));
			return xDoc;
		}
		public static XmlElement BuildSitemapUrlNode(ref XmlDocument xDoc, string _loc, DateTime? _lastmod, string _changefreq, string _priority)
		{
			XmlElement
				url = xDoc.CreateElement("url"),
				loc = xDoc.CreateElement("loc"),
				lastmod = _lastmod == null ? null : xDoc.CreateElement("lastmod"),
				changefreq = xDoc.CreateElement("changefreq"),
				priority = xDoc.CreateElement("priority");
			
			loc.InnerText = _loc;
			url.AppendChild(loc);

			changefreq.InnerText = _changefreq;
			url.AppendChild(changefreq);

			if (_lastmod != null) {
				lastmod.InnerText = _lastmod.Value.ToString("yyyy-MM-dd");
				url.AppendChild(lastmod); }
			if (!string.IsNullOrEmpty(_priority)) {
				priority.InnerText = _priority;
				url.AppendChild(priority); }

			url.Attributes.RemoveAll();
			return url;
		}
		public static XmlDocument BuildSitemapXML(ref BlogSlingerDataContext dc, int BlogID)
		{
			foreach (Post post in (from p in dc.Posts where p.BlogID == BlogID orderby p.PublishDate descending select p).Take(10)) 
			{


			}

			return null;
		}
		public static string FormatXml(XmlDocument doc)
		{ 
			StringBuilder sb = new StringBuilder();
			XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() {
				Indent = true,
				IndentChars = "\t",
				NewLineChars = "\r\n",
				NewLineHandling = NewLineHandling.Replace }); 
			doc.Save(writer); 
			writer.Close(); 
			return sb.ToString(); 
		}
		public static XmlDocument BuildSitemapIndex(ref BlogSlingerDataContext dc)
		{
			XmlDocument xDoc = new XmlDocument();
			XmlElement xRoot = xDoc.CreateElement("sitemapindex", @"http://www.sitemaps.org/schemas/sitemap/0.9");
			xDoc.AppendChild(xRoot);

			return null;
		}
		public static XmlElement BuildSitemapIndexNode(ref XmlDocument xDoc, string loc, DateTime? lastmod)
		{
			return null;
			//XmlElement 
		}
	}
}
